#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <strings.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
#include <fcntl.h>
#include <linux/types.h>
#include <mtd/mtd-user.h>

#include "LIB_Markup.h"

typedef struct{
	bool bValid;
	char sMtd[64];
	char sVer[64];
	int nCRC;
} ST_MTD_BOOT_T, *LPST_MTD_BOOT_T;

typedef struct{
	const char* pTZ;
	const char* pTZ_in;
	int nTZ;
} ST_TZ_T, *LPST_TZ_T;

const ST_TZ_T defTZ[] = {
	{"GMT+13", "GMT-13", 13 * 3600},
	{"GMT+12", "GMT-12", 12 * 3600},
	{"GMT+11", "GMT-11", 11 * 3600},
	{"GMT+10", "GMT-10", 10 * 3600},
	{"GMT+9", "GMT-9", 9 * 3600},
	{"GMT+8", "GMT-8", 8 * 3600},
	{"GMT+7", "GMT-7", 7 * 3600},
	{"GMT+6", "GMT-6", 6 * 3600},
	{"GMT+5", "GMT-5", 5 * 3600},
	{"GMT+4", "GMT-4", 4 * 3600},
	{"GMT+3", "GMT-3", 3 * 3600},
	{"GMT+2", "GMT-2", 2 * 3600},
	{"GMT+1", "GMT-1", 1 * 3600},
	{"GMT", "GMT", 0},
	{"GMT-1", "GMT+1", -1 * 3600},
	{"GMT-2", "GMT+2", -2 * 3600},
	{"GMT-3", "GMT+3", -3 * 3600},
	{"GMT-4", "GMT+2", -4 * 3600},
	{"GMT-5", "GMT+5", -5 * 3600},
	{"GMT-6", "GMT+6", -6 * 3600},
	{"GMT-7", "GMT+7", -7 * 3600},
	{"GMT-8", "GMT+8", -8 * 3600},
	{"GMT-9", "GMT+9", -9 * 3600},
	{"GMT-10", "GMT+10", -10 * 3600},
	{"GMT-11", "GMT+11", -11 * 3600},
	{"GMT-12", "GMT+12", -12 * 3600},
	{"", "", 0},};

bool FlashEraseBlock(int ndev, unsigned long nblockaddr, unsigned long nerasesize)
{
	struct erase_info_user erase;
	erase.start = nblockaddr;
	erase.length = nerasesize;
	if (ioctl(ndev, MEMERASE, &erase) < 0){
		printf("Operate mtd MEMERASE failed!\n");
		return false;
	}
	return true;
}

void Upgrade(char* pMtd, char* pUpgradeFile)
{
	int ndev = -1;
	struct mtd_info_user mtd;
	unsigned char* pBuffer = NULL;
	int i, nReadLen;
	FILE* fp = NULL;
	
	printf("Start upgrade %s with %s\n", pMtd, pUpgradeFile);
	fp = fopen(pUpgradeFile, "r");
	if (fp == NULL){
		printf("Open %s failed!\n", pUpgradeFile);
		goto END_UPGRADE;
	}
	if ((ndev = open(pMtd, O_RDWR | O_SYNC)) < 0) {
		printf("Open %s failed!", pMtd);
		goto END_UPGRADE;
	}
	if (ioctl(ndev, MEMGETINFO, &mtd) < 0){
		printf("Operate MEMGETINFO failed!\n");
		goto END_UPGRADE;
	}
	pBuffer = (unsigned char*)malloc(mtd.erasesize);
	printf("Erasing");
	fflush(stdout);
	for (i = 0; i < mtd.size; i += mtd.erasesize){
		if (!FlashEraseBlock(ndev, i, mtd.erasesize))
			goto END_UPGRADE;
		printf(".");
		fflush(stdout);
	}
	printf("\nWriting");
	do{
		nReadLen = fread(pBuffer, 1, mtd.erasesize, fp);
		if (nReadLen > 0){
			if (write(ndev, pBuffer, nReadLen) < 0){
				printf("Operate mtd write failed!\n");
				break;
			}
			else{
				printf(".");
				fflush(stdout);
			}
		}
	}while(nReadLen > 0);
	printf("\n");
END_UPGRADE:
	if (fp)
		fclose(fp);
	if (pBuffer)
		free(pBuffer);
	if (ndev != -1)
		close(ndev);
	printf("End upgrade\n");
}

void CVT_TZ(char* pTZ, LPST_TZ_T pTZRet)
{
	int i = 0;
	while (strlen(defTZ[i].pTZ) > 0){
		if (strcmp(pTZ, defTZ[i].pTZ) == 0){
			memcpy(pTZRet, &defTZ[i], sizeof(ST_TZ_T));
			return;
		}
		else if (defTZ[i].nTZ == 0){
			memcpy(pTZRet, &defTZ[i], sizeof(ST_TZ_T));
		}
		i++;
	}
}

void SET_TZ(char* pTZ)
{
	char sCmd[256];
	sprintf(sCmd, "ln -sf /share/etc/%s /tmp/localtime", pTZ);
	system(sCmd);
}

void SetTz()
{
	CMarkup xml;
	if (!xml.Load("/mnt/mtd/workparam_mixdvr.xml")){
		system("ln -s /share/etc/GMT-8 /tmp/localtime");
	}
	else{
		if (!xml.FindElem("Message"))
			system("ln -s /share/etc/GMT-8 /tmp/localtime");
		else{
			xml.IntoElem();
			if (!xml.FindElem("SYSTEM"))
				system("ln -s /share/etc/GMT-8 /tmp/localtime");
			else{
				xml.IntoElem();
				if (!xml.FindElem("SYSTIME"))
					system("ln -s /share/etc/GMT-8 /tmp/localtime");
				else{
					char sTz[32];
					ST_TZ_T nTZ;
					strcpy(sTz, xml.GetAttrib("TZ").c_str());
					CVT_TZ(sTz, &nTZ);
					SET_TZ((char*)nTZ.pTZ_in);
				}
			}
		}
	}
}

int main(int argc, char* argv[])
{
	int ndev = -1;
	unsigned char* pBuffer = NULL;
	struct mtd_info_user mtd;
	int i, j, nRet;
	unsigned int nChecksum = 0;
	CMarkup xml;
	ST_MTD_BOOT_T nBootMtd[2];

	nRet = 0;

	if (argc == 4){
		if (strcmp(argv[1], "upgrade") == 0)
			Upgrade(argv[2], argv[3]);
		return 0;
	}
	if (argc == 2){
		if (strcmp(argv[1], "settz") == 0)
			SetTz();
		return 0;
	}

	if (!xml.Load("/mnt/mtd/appinfo.xml")){
		printf("Invalid appinfo.xml\n");
		xml.SetDoc("");
		xml.AddElem("APPINFO");
		xml.IntoElem();
		xml.AddElem("MTDVER");
		xml.AddAttrib("ID", "/dev/mtd4");
		xml.AddAttrib("VER", "0");
		xml.AddAttrib("CRC", "0");
		xml.AddElem("MTDVER");
		xml.AddAttrib("ID", "/dev/mtd5");
		xml.AddAttrib("VER", "0");
		xml.AddAttrib("CRC", "0");
		xml.OutOfElem();
		xml.Save("/mnt/mtd/appinfo.xml");
		xml.ResetPos();
	}
	xml.FindElem("APPINFO");
	xml.IntoElem();
		xml.FindElem("MTDVER");
		nBootMtd[0].bValid = false;
		strcpy(nBootMtd[0].sMtd, xml.GetAttrib("ID").c_str());
		strcpy(nBootMtd[0].sVer, xml.GetAttrib("VER").c_str());
		nBootMtd[0].nCRC = atoi(xml.GetAttrib("CRC").c_str());
		xml.FindElem("MTDVER");
		nBootMtd[1].bValid = false;
		strcpy(nBootMtd[1].sMtd, xml.GetAttrib("ID").c_str());
		strcpy(nBootMtd[1].sVer, xml.GetAttrib("VER").c_str());
		nBootMtd[1].nCRC = atoi(xml.GetAttrib("CRC").c_str());
	xml.OutOfElem();

	for (i = 0; i < 2; i++){
		printf("Check MTD %s CRC\n", nBootMtd[i].sMtd);
		if ((ndev = open(nBootMtd[i].sMtd, O_RDWR | O_SYNC)) < 0) {
			printf("Open %s failed!", nBootMtd[i].sMtd);
			continue;
		}
		if (ioctl(ndev, MEMGETINFO, &mtd) < 0){
			printf("Operate MEMGETINFO failed!\n");
			close(ndev);
			continue;
		}
		pBuffer = (unsigned char*)malloc(mtd.erasesize);
		nChecksum = 0;
		for (j = 0; j < mtd.size; j += mtd.erasesize){
			int nCount = read(ndev, pBuffer, mtd.erasesize);
			if (nCount != mtd.erasesize){
				printf("Read %d block failed!\n", i);
				break;
			}
			while(nCount-- >= 0){
				nChecksum += pBuffer[nCount];
			}
		}
		nBootMtd[i].bValid = (nBootMtd[i].nCRC == nChecksum);
		printf("%s checksum = %d, boot valid = %d\n", nBootMtd[i].sMtd, nChecksum, nBootMtd[i].bValid);
		free(pBuffer);
		close(ndev);
	}

	if (nBootMtd[0].bValid && nBootMtd[1].bValid){
		if (strcmp(nBootMtd[0].sVer, nBootMtd[1].sVer) >= 0)
			nRet = 0;
		else
			nRet = 1;
	}
	else if (nBootMtd[0].bValid)
		nRet = 0;
	else
		nRet = 1;
	printf("boot mtd = %s\n", nBootMtd[nRet].sMtd);
	return nRet;
}

